package org.yunai.swjg.server.module.item.container;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.role.Role;
import org.yunai.swjg.server.module.item.template.ItemTemplate;
import org.yunai.swjg.server.module.item.vo.Item;
import org.yunai.yfserver.common.LoggerFactory;

import java.util.*;

/**
 * 通用的道具容器实现
 * <p>提供了调整容量、整理、丢弃道具、找空位的通用实现。背包和仓库租赁箱都可以在此基础上实现。</p>
 * User: yunai
 * Date: 13-4-6
 * Time: 下午4:44
 */
public abstract class CommonBag extends AbstractItemBag {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.item, CommonBag.class);

    /**
     * 整理背包用的排序器<br />
     * 一级分类(升序) > 二级分类(升序) > 模版Id(升序) > 堆叠(降序)
     */
    private final Comparator<Item> tidyUpComp = new Comparator<Item>() {

        @Override
        public int compare(Item itemA, Item itemB) {
            if (itemA == null) {
                return itemB == null ? 0 : 1;
            }
            if (itemB == null) {
                return -1;
            }
            ItemTemplate templateA = itemA.getTemplate();
            ItemTemplate templateB = itemB.getTemplate();
            if (templateA != templateB) {
                if (templateA.getIdentityType() != templateB.getIdentityType()) { // 一级分类
                    return templateA.getIdentityType().getIndex() - templateB.getIdentityType().getIndex();
                }
                if (templateA.getType() != templateB.getType()) { // 二级分类
                    return templateA.getType().getIndex() - templateB.getType().getIndex();
                }
                return templateA.getId() - templateB.getId(); // 模版ID
            }
            // 同一个模板，按叠加数排序
            return itemB.getOverlap() - itemA.getOverlap();
        }
    };

    protected CommonBag(Role owner, BagType bagType, int capacity) {
        super(owner, bagType, capacity);
    }

//    public boolean resetCapacity(int newCapacity) {

    /**
     * 整理背包
     * TODO 该方法会在触发背包物品数量改变时，产生BUG，想办法处理下
     */
    public void packUp() {
        // 合并可叠加的道具，尽可能叠加
        List<Item> mergedList = mergeSameTemplateItems();
        // 按照排序规则排序
        Collections.sort(mergedList, tidyUpComp);
        // 重设所有道具的索引，并放回背包中
        Iterator<Item> iterator = mergedList.iterator();
        int cap = getCapacity();
        // 格子赋值
        Item[] newItems = new Item[cap];
        int i = 0;
        while (iterator.hasNext()) {
            newItems[i] = iterator.next();
            newItems[i].changeIndex(i);
            i++;
        }
        this.items = newItems;
    }

    /**
     * 将背包中所有非空的Item进行合并<br />
     * 尽可能的叠加
     *
     * @return 合并后的Item列表
     */
    private List<Item> mergeSameTemplateItems() {
        // 先按模板ID进行分类
        Map<Integer, List<Item>> classifyMap = classifyByTemplate();
        // 开始合并
        List<Item> mergedList = new ArrayList<>(getCapacity());
        for (List<Item> items : classifyMap.values()) {
            mergedList.addAll(mergeSameTemplateItems(items));
        }
        return mergedList;
    }

    /**
     * 按模板Id进行分类，组成map
     *
     * @return map<模版编号, 该模版对应的玩家道具列表>
     */
    private Map<Integer, List<Item>> classifyByTemplate() {
        Map<Integer, List<Item>> classifyMap = new HashMap<Integer, List<Item>>();
        for (Item item : items) {
            if (Item.isEmpty(item)) {
                continue;
            }
            List<Item> items = classifyMap.get(item.getTemplateId());
            if (items == null) {
                classifyMap.put(item.getTemplate().getId(), items = new ArrayList<Item>());
            }
            items.add(item);
        }
        return classifyMap;
    }

    /**
     * 合并相同模版的玩家道具
     *
     * @param items 相同模版的玩家道具
     * @return 合并后结果
     */
    private Collection<Item> mergeSameTemplateItems(List<Item> items) {
        // 统计一共有多少个
        int total = 0;
        for (Item item : items) {
            total += item.getOverlap();
        }
        // 取得最大叠加数
        int maxOverlap = items.get(0).getMaxOverlap();
        // 合并开始
        int needSlot = total % maxOverlap == 0 ? total / maxOverlap : total / maxOverlap + 1;
        List<Item> results = new ArrayList<Item>(needSlot);
        for (Item item : items) {
            if (total >= maxOverlap) {
                item.changeOverlap(maxOverlap);
                total -= maxOverlap;
                results.add(item);
            } else if (total > 0) {
                item.changeOverlap(total);
                total = 0;
                results.add(item);
            } else {
                item.changeOverlap(0);
            }
        }
        return results;
    }

    /**
     * 丢弃指定位置的道具
     *
     * @param index 位置
     * @return 丢弃的道具信息
     */
    public Item drop(int index) {
        Item item = super.getByIndex(index);
        if (Item.isEmpty(item)) {
            return null;
        }
        item.changeOverlap(0);
        return item;
    }

//     public Item getCanPutSolt(ItemTemplate template, int count, BindStatus bindStatus) {

//    private Item getCanPutOverlapSlot(int templateId, int count,
//                                      BindStatus bindStatus) {

    /**
     * 将背包中一个物品移动到另外一个背包的指定位置, 并返回移动结果<br />
     *
     * @param item      物品
     * @param targetBag 目标背包
     * @param index     指定位置
     * @return 移动是否成功
     */
    public boolean move(Item item, AbstractItemBag targetBag, int index) {
        if (index < 0) {
            LOGGER.error("[move] [item({}) move to role({})'bag({}) index({})]", item.getId(), targetBag.getOwner().getId(),
                    targetBag.getBagType(), index);
            return false;
        }
        // 检查指定位置是否为空
        Item emptyItem = targetBag.getByIndex(index);
        if (emptyItem != null) {
            return false;
        }
        // 修改item信息(位置+背包)
        item.changeBagType(targetBag.getBagType(), index);
        // 放到背包里
        targetBag.putItem(item);
        return true;
    }

    /**
     * 将背包中一个物品移动到另外一个背包的第一个空位上, 并返回移动结果
     *
     * @param item      物品
     * @param targetBag 目标背包
     * @return 移动是否成功
     */
    public boolean move(Item item, AbstractItemBag targetBag) {
        // 获得第一个空位
        int index = targetBag.getFirstEmptyIndex();
        if (index == BAG_INDEX_EMPTY) {
            return false;
        }
        // 移动
        return move(item, targetBag, index);
    }
}